package com.lizemin.spring.mvc;


import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.lizemin.spring.BeanFactory;
import com.lizemin.spring.annotation.GetMapping;
import com.lizemin.spring.annotation.RequestMapping;
import com.lizemin.spring.annotation.RestController;
import com.lizemin.spring.util.ConvertionService;
import lombok.SneakyThrows;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class DispatchServlet extends HttpServlet {

    /**
     * 保存url和handler的映射关系
     * key：url
     * value：handler
     */
    private static final ConcurrentHashMap<String, Handler> handlerMapping = new ConcurrentHashMap<>();

    /**
     * 只有第一个请求才会初始化调用init方法，后续不会调用
     *
     * @param config ServletConfig对象，用于获取Servlet的配置信息
     */
    @Override
    public void init(ServletConfig config) {
        String contextPath = config.getServletContext().getContextPath();
        // 在这里建立url和handler的映射关系
        Set<Class<?>> clazz = BeanFactory.getBeanClassWithAnnotation(RestController.class);
        for (Class<?> controller : clazz) {
            String url = contextPath;
            RequestMapping requestMapping = controller.getAnnotation(RequestMapping.class);
            if (Objects.nonNull(requestMapping)) {
                url = url + requestMapping.value();
            }
            Method[] methods = controller.getDeclaredMethods();
            for (Method method : methods) {
                GetMapping getMapping = method.getAnnotation(GetMapping.class);
                if (Objects.nonNull(getMapping)) {
                    String subUrl = getMapping.value();
                    Object bean = BeanFactory.getBean(controller);
                    Handler handler = new Handler(method, bean);
                    handlerMapping.put(url + subUrl, handler);
                }
            }
        }
    }

    @SneakyThrows
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String uri = req.getRequestURI();
        Handler handler = handlerMapping.get(uri);
        if (Objects.isNull(handler)) {
            resp.setContentType("text/html");
            resp.setCharacterEncoding("utf-8");
            resp.getWriter().println("<html><body>404 not found</h1></body></html>");
            return;
        }
        String query = req.getQueryString();
        HashMap<String, Object> queryMap = new HashMap<>();
        if (StrUtil.isNotBlank(query)) {
            String[] queryArr = query.split("&");
            for (String qry : queryArr) {
                qry = qry.replace("?", "");
                String[] kv = qry.split("=");
                queryMap.put(kv[0], kv[1]);
            }
        }
        Method method = handler.getMethod();
        int count = method.getParameterCount();
        Object bean = handler.getBean();
        Class<?>[] parameterTypes = method.getParameterTypes();
        Parameter[] parameters = method.getParameters();
        Object[] args = new Object[count];
        for (int i = 0; i < parameters.length; i++) {
            String name = parameters[i].getName();
            Object queryVal = queryMap.get(name);
            Object targetVal = ConvertionService.convert(queryVal, parameterTypes[i]);
            args[i] = targetVal;
        }
        Object result;
        if (count > 0) {
            result = method.invoke(bean, args);
        } else {
            result = method.invoke(bean);
        }
        resp.setContentType("application/json");
        resp.setCharacterEncoding("utf-8");
        if (Objects.nonNull(result)) {
            resp.getWriter().println(JSONUtil.toJsonStr(result));
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }

}